<template>
  <div id="diagram" style="width: 100%; height: 650px; background-color: #fafafa"></div>
</template>

<script setup>
  import go from 'gojs'
  import { onMounted, onBeforeMount } from 'vue'
  import bus from 'vue3-eventbus'
  import { getTopoModuleList } from '/@/api/module/topoModule'

  let diagram = null

  function parseData(data) {
    let nodes = []
    let links = []
    let linkStates = []

    if (data && data.modules) {
      for (let mod of data.modules) {
        let node = {
          key: mod.name,
          fields: [],
          loc: '0 0',
        }

        if (mod.state === 'inactive') {
          node.fill = 'red'
        }

        if (mod.links) {
          for (let link of mod.links) {
            let field = {
              name: link.moduleName,
              info: '',
              figure: 'ellipse',
            }

            if (link.state === 0) {
              field.color = 'green'
            } else if (link.state === 1) {
              field.color = 'red'
            } else if (link.state === 2) {
              field.color = '#F7B84B'
            }

            node.fields.push(field)

            links.push({
              from: mod.name,
              fromPort: '',
              to: link.moduleName,
              toPort: '',
              text: link.protocol,
            })

            linkStates.push({
              state: link.state,
              from: mod.name,
              to: link.moduleName,
            })
          }
        }

        nodes.push(node)
      }
    }

    return { nodes, links, linkStates }
  }

  // 根据 state 更新 link 颜色
  function updateLinkStyle(linkStates) {
    for (let linkState of linkStates) {
      let fromNode = diagram.findNodeForKey(linkState.from)
      let toNode = diagram.findNodeForKey(linkState.to)
      let link = fromNode.findLinksTo(toNode).first()
      if (link != null) {
        if (linkState.state == 0) {
          link.findObject('line').stroke = 'green'
          link.findObject('arrow').stroke = 'green'
        } else if (linkState.state == 1) {
          link.findObject('line').stroke = 'red'
          link.findObject('arrow').stroke = 'red'
        } else if (linkState.state == 2) {
          link.findObject('line').stroke = '#F7B84B'
          link.findObject('arrow').stroke = '#F7B84B'
        } else {
          console.log('invalid state:', linkState.state)
        }
      } else {
        console.log(`Link not found: ${linkState.from} -> ${linkState.to}`)
      }
    }

    diagram.commit(function (diag) {
      diag.links.each(function (link) {
        link.updateTargetBindings()
        link.updateRoute()
      })
    }, 'modified link colors')
  }

  function updateDiagramData(data) {
    if (!data || Object.keys(data).length === 0) {
      console.warn('invalid data')
      return
    }

    let { nodes, links, linkStates } = parseData(data)

    // 设置模型数据
    diagram.model = new go.GraphLinksModel({
      copiesArrays: true,
      copiesArrayObjects: true,
      linkFromPortIdProperty: 'fromPort',
      linkToPortIdProperty: 'toPort',
      nodeDataArray: nodes,
      linkDataArray: links,
    })

    // 设置连接颜色
    updateLinkStyle(linkStates)
  }

  onBeforeMount(() => {
    bus.on('updateGraph', (topoViewId) => {
      if (topoViewId === 0) {
        diagram.model = new go.GraphLinksModel({
          copiesArrays: true,
          copiesArrayObjects: true,
          linkFromPortIdProperty: 'fromPort',
          linkToPortIdProperty: 'toPort',
        })
      } else {
        getTopoModuleList({ view: topoViewId })
          .then((data) => {
            updateDiagramData(data)
          })
          .catch((e) => {
            console.log('get module list failed:', e)
          })
      }
    })
  })

  // 页面挂载后再初始化，否则无法找到 diagram 挂载节点
  onMounted(() => {
    // 创建 diagram
    diagram = new go.Diagram('diagram', {
      'undoManager.isEnabled': false, // enable Ctrl-Z to undo and Ctrl-Y to redo
    })

    diagram.initialContentAlignment = go.Spot.Center
    diagram.contentAlignment = go.Spot.Center
    diagram.initialAutoScale = go.Diagram.Uniform

    // 设置画布的宽度和高度
    //diagram.div.style.width = "800px";
    //diagram.div.style.height = "650px";

    // 禁用滚动条
    diagram.scrollMode = go.Diagram.InfiniteScroll

    // 设置 layout
    diagram.layout = new go.ForceDirectedLayout()

    // 设置 link 模板
    diagram.linkTemplate = new go.Link({
      relinkableFrom: true,
      relinkableTo: true,
      toShortLength: 4,
    })
      .add(
        new go.Shape(
          {
            strokeWidth: 3,
            stroke: 'green',
          },
          {
            name: 'line',
          },
        ),
      )
      .add(
        new go.Shape(
          {
            toArrow: 'Standard',
            stroke: 'green',
          },
          {
            name: 'arrow',
          },
        ),
      )
      .add(
        new go.TextBlock({
          name: 'Link Text',
        }).bind('text', 'text'),
      )

    // 设置 content 模板
    let fieldTemplate = new go.Panel('TableRow', new go.Binding('portId', 'name'), {
      // so this port's background can be picked by the mouse
      background: 'blue',
      // links only go from the right side to the left side
      fromSpot: go.Spot.LeftRightSides,
      toSpot: go.Spot.LeftRightSides,
      // allow drawing links from or to this port:
      fromLinkable: true,
      toLinkable: true,
    })
      .add(
        new go.Shape({
          width: 12,
          height: 12,
          column: 0,
          strokeWidth: 2,
          margin: 4,
          fromLinkable: false,
          toLinkable: false,
        })
          .bind('figure', 'figure')
          .bind('fill', 'color'),
      )
      .add(
        new go.TextBlock({
          margin: new go.Margin(0, 2),
          column: 1,
          font: 'bold 13px sans-serif',
          fromLinkable: false,
          toLinkable: false,
          alignment: go.Spot.Left,
        }).bind('text', 'name'),
      )
      .add(
        new go.TextBlock({
          margin: new go.Margin(0, 2),
          column: 2,
          font: '13px sans-serif',
          alignment: go.Spot.Left,
        }).bind('text', 'info'),
      )

    // 设置 Node 模板
    diagram.nodeTemplate = new go.Node(
      'Auto',
      new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify),
    )
      .add(new go.Shape({ fill: '#EEEEEE' }))
      .add(
        new go.Panel('Vertical')
          .add(
            new go.Panel('Auto', { stretch: go.GraphObject.Horizontal })
              .add(
                new go.Shape({
                  fill: '#1570A6',
                  stroke: null,
                }).bind('fill', 'fill'),
              )
              .add(
                new go.TextBlock({
                  alignment: go.Spot.Center,
                  margin: 3,
                  stroke: 'white',
                  textAlign: 'center',
                  font: 'bold 12pt sans-serif',
                }).bind('text', 'key'),
              ),
          )
          .add(
            new go.Panel('Table', {
              name: 'TABLE',
              padding: 2,
              minSize: new go.Size(100, 10),
              defaultStretch: go.GraphObject.Horizontal,
              itemTemplate: fieldTemplate,
            }).bind('itemArray', 'fields'),
          ),
      )
  })
</script>
